package com.guijianpan.framework.repository;

import java.math.BigDecimal;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;

import com.guijianpan.framework.domain.BaseDomain;

/**
 * 基于原生SQL查询的基础Repository
 * 
 * @author yzChen  2014-8-26 11:09:43
 * @param <T>
 */
public class BaseSQLRepository<T extends BaseDomain> {

	@PersistenceContext
	private EntityManager em;

	public void setEntityManager(EntityManager em) {
		this.em = em;
	}
	
	/**
	 * sql语句查询byId
	 * @param targetClass
	 * @param sql
	 * @param id
	 * @return
	 * @throws EmptyResultDataAccessException
	 */
	@SuppressWarnings("unchecked")
	protected T queryById(Class<T> targetClass, String sql, String id) throws EmptyResultDataAccessException {
		Query query = em.createNativeQuery(sql, targetClass);
		query.setParameter(0, id);
		return (T) query.getSingleResult();
	}
	
	/**
	 * 根据原生sql，条件查询，返回所有数据的List
	 * 
	 * @param targetClass	返回目标domain类型
	 * @param sql				查询sql
	 * @param args			参数列表
	 * @return
	 * @author zyChen  2014-8-26 11:12:18
	 */
	@SuppressWarnings("unchecked")
	protected List<T> queryList(Class<T> targetClass, String sql, Object... args) {
		Query query = em.createNativeQuery(sql, targetClass);
		if (null != args) {
			for (int i = 0; i < args.length; i++) {
				query.setParameter(i, args[i]);
			}
		}
		return query.getResultList();
	}


	/**
	 * 根据原生sql，条件查询，返回分页数据的Page
	 * 
	 * @param pageNo		当前页数
	 * @param pageSize		分页条数
	 * @param targetClass	返回目标domain类型
	 * @param sql				查询sql
	 * @param args			参数列表
	 * @return
	 * @author zyChen  2014-8-26 11:12:18
	 */
	protected Page<T> queryPage(int pageNo, int pageSize, Class<T> targetClass, String sql, Object... args) {
		return this.queryPage(pageNo, pageSize, targetClass, sql, null, args);
	}
	
	/**
	 * 根据原生sql，条件查询，返回分页数据的Page，需要排序对象参数（默认Mysql）
	 * 
	 * @param databaseType		默认mysql
	 * @param pageNo		当前页数
	 * @param pageSize		分页条数
	 * @param targetClass	返回目标domain类型
	 * @param sql				查询sql
	 * @param sort				排序规则
	 * 										（由于是sql，所以排序是在sql中，而不是此方法封装，若页面调用Repository方需要Sort对象，
	 * 											则在Repository实例化为参数传入，本方法在实例Page对象时调用）
	 * @param args			参数列表
	 * @return
	 * @author zyChen  2015-3-28 10:16:08
	 */
	protected Page<T> queryPage(final DbType databaseType, int pageNo, int pageSize, Class<T> targetClass, String sql, Sort sort, Object... args) {
		DbType dbType = null == databaseType ? databaseType : DbType.MYSQL;
		if(DbType.ORACLE == dbType) {
			return queryPage4Oracle(pageNo, pageSize, targetClass, sql, sort, args);
		}
		return queryPage4Mysql(pageNo, pageSize, targetClass, sql, sort, args);
			
	}

	/**
	 * 根据原生sql，条件查询，返回分页数据的Page，需要排序对象参数（Oracle）
	 * 
	 * @param pageNo		当前页数
	 * @param pageSize		分页条数
	 * @param targetClass	返回目标domain类型
	 * @param sql				查询sql
	 * @param sort				排序规则
	 * 										（由于是sql，所以排序是在sql中，而不是此方法封装，若页面调用Repository方需要Sort对象，
	 * 											则在Repository实例化为参数传入，本方法在实例Page对象时调用）
	 * @param args			参数列表
	 * @return
	 * @author zyChen  2014-8-26 11:12:18
	 */
	@SuppressWarnings("unchecked")
	protected Page<T> queryPage4Oracle(int pageNo, int pageSize, Class<T> targetClass, String sql, Sort sort, Object... args) {
		// 添加分页
		StringBuffer exuesql = new StringBuffer();
		exuesql.append("select * from (select t_1.*, rownum rn_1 from (")
				.append(sql).append(") t_1) where rn_1 > (")
				.append(pageNo)
				.append("-1) * ")
				.append(pageSize).append(" and rn_1 <= ")
				.append(pageNo+1)
				.append(" * ")
				.append(pageSize);
		Query query = em.createNativeQuery(exuesql.toString(), targetClass);
		if (null != args) {
			for (int i = 0; i < args.length; i++) {
				query.setParameter(i, args[i]);
			}
		}
		List<T> list = query.getResultList();
		
		// 获取总数
		String countQueryString = " select count(*) cc from (" + sql + ")";
		Query countQuery = em.createNativeQuery(countQueryString);
		for (int i = 0; i < args.length; i++) {
			countQuery.setParameter(i, args[i]);
		}
		BigDecimal totalCount = (BigDecimal) countQuery.getSingleResult();
		
		// 生成返回值
		Page<T> page = new PageImpl<>(list, new PageRequest(pageNo, pageSize,
				sort), totalCount.longValue());
		
		return page;
	}
	


	/**
	 * 根据原生sql，条件查询，返回分页数据的Page，需要排序对象参数（Mysql）
	 * 
	 * @param pageNo		当前页数
	 * @param pageSize		分页条数
	 * @param targetClass	返回目标domain类型
	 * @param sql				查询sql
	 * @param sort				排序规则
	 * 										（由于是sql，所以排序是在sql中，而不是此方法封装，若页面调用Repository方需要Sort对象，
	 * 											则在Repository实例化为参数传入，本方法在实例Page对象时调用）
	 * @param args			参数列表
	 * @return
	 * @author zyChen  2014-8-26 11:12:18
	 */
	protected Page<T> queryPage4Mysql(int pageNo, int pageSize, Class<T> targetClass, String sql, Sort sort, Object... args) {
		
		return null;
	}
	
	
}
